package com.ctw.tinyservices.id.server.snowflake.dao.impl;

import com.ctw.tinyservices.id.common.entity.IdWorkerId;
import com.ctw.tinyservices.id.common.utils.LogUtils;
import com.ctw.tinyservices.id.server.snowflake.dao.IdWorkerIdDAO;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author TongWei.Chen 2022/3/3 13:34
 *
 * Id号段DAO具体实现
 **/
public class IdWorkerIdDAOImpl implements IdWorkerIdDAO {
    private JdbcTemplate jdbcTemplate;
    private DataSourceTransactionManager transactionManager;

    public IdWorkerIdDAOImpl(JdbcTemplate jdbcTemplate, DataSourceTransactionManager transactionManager) {
        this.jdbcTemplate = jdbcTemplate;
        this.transactionManager = transactionManager;
    }

    @Override
    public IdWorkerId getOrInsert(String ip, int port, String ipPort) {
        LogUtils.info(IdWorkerIdDAOImpl.class, "[getOrInsert] method begin, ip is [{}], port is [{}]", ip, port);
        TransactionStatus status = null;
        List<IdWorkerId> idWorkerIdList;
        try {
            status = begin();
            idWorkerIdList = jdbcTemplate.query("SELECT id, ip, port, ip_port, max_timestamp FROM ts_id_worker_id WHERE ip_port = ?", new Object[]{ipPort}, new IdWorkerIdRowMapper());
            if (null == idWorkerIdList || idWorkerIdList.size() == 0) {
                jdbcTemplate.update("INSERT INTO ts_id_worker_id(ip, port, ip_port, max_timestamp) VALUES(?, ?, ?, ?)", new Object[]{ip, port, ipPort, System.currentTimeMillis()});
                idWorkerIdList = jdbcTemplate.query("SELECT id, ip, port, ip_port, max_timestamp FROM ts_id_worker_id WHERE ip_port = ?", new Object[]{ipPort}, new IdWorkerIdRowMapper());
            }
            commit(status);
            return idWorkerIdList.get(0);
        } catch (Exception e) {
            LogUtils.error(IdWorkerIdDAOImpl.class, "[getOrInsert] method error and transaction rollback, ip is [{}], port is [{}]", ip, port, e);
            rollback(status);
            throw e;
        }
    }

    @Override
    public int updateMaxTimestamp(int workerId, long currentTime) {
        LogUtils.info(IdWorkerIdDAOImpl.class, "[updateMaxTimestamp] method begin, workerId is [{}], currentTime is [{}]", workerId, currentTime);
        TransactionStatus status = null;
        try {
            status = begin();
            jdbcTemplate.update("UPDATE ts_id_worker_id SET max_timestamp = ? WHERE id = ?", new Object[]{currentTime, workerId});
            commit(status);
        } catch (Exception e) {
            LogUtils.error(IdWorkerIdDAOImpl.class, "[updateMaxTimestamp] method error and transaction rollback, workerId is [{}], currentTime is [{}]", workerId, currentTime, e);
            rollback(status);
            throw e;
        }
        return 0;
    }

    /**
     * 开启事务
     */
    public TransactionStatus begin(){
        return beginTransaction(transactionManager);
    }

    /**
     * 提交事务
     * @param status
     */
    public void commit(TransactionStatus status){
        commitTransaction(transactionManager,status);
    }

    /**
     * 回滚事务
     * @param status
     */
    public void rollback(TransactionStatus status){
        rollbackTransaction(transactionManager,status);
    }

    /**
     * 开启事务
     */
    public TransactionStatus beginTransaction(DataSourceTransactionManager transactionManager){
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        return transactionManager.getTransaction(def);
    }

    /**
     * 提交事务
     * @param transactionManager
     * @param status
     */
    public void commitTransaction(DataSourceTransactionManager transactionManager,TransactionStatus status){
        transactionManager.commit(status);
    }

    /**
     * 事务回滚
     * @param transactionManager
     * @param status
     */
    public void rollbackTransaction(DataSourceTransactionManager transactionManager,TransactionStatus status){
        transactionManager.rollback(status);
    }

    private static class IdWorkerIdRowMapper implements RowMapper<IdWorkerId> {
        @Override
        public IdWorkerId mapRow(ResultSet resultSet, int i) throws SQLException {
            IdWorkerId idWorkerId = new IdWorkerId();
            idWorkerId.setId(resultSet.getInt("id"));
            idWorkerId.setIp(resultSet.getString("ip"));
            idWorkerId.setPort(resultSet.getInt("port"));
            idWorkerId.setIpPort(resultSet.getString("ip_port"));
            idWorkerId.setMaxTimestamp(resultSet.getLong("max_timestamp"));
            return idWorkerId;
        }
    }

}
